package ch12.executors;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class ExecutorDemo {
    public static void main(String[] args)
        throws InterruptedException, ExecutionException, IOException {
        try (var in = new Scanner(System.in)) {
            // 输入目录路径
            System.out.print("Enter base directory (e.g. ch12): ");
            String directory = in.nextLine();

            // 输入关键字
            System.out.print("Enter keyword (e.g. volatile): ");
            String word = in.nextLine();

            // 获取目录下所有文件路径
            Set<Path> files = descendants(Path.of(directory));

            // 创建统计关键字数量的任务列表
            var tasks = new ArrayList<Callable<Long>>();
            for (Path file : files) {
                Callable<Long> task = () -> occurrences(word, file);
                tasks.add(task);
            }

            // 创建线程池
            ExecutorService executor = Executors.newCachedThreadPool();

            // 开始时间
            Instant startTime = Instant.now();

            // 执行任务
            List<Future<Long>> results = executor.invokeAll(tasks);

            long total = 0;
            for (Future<Long> result : results) {
                total += result.get();
            }

            // 花费时间
            Instant endTime = Instant.now();
            System.out.println("Occurences of " + word + ": " + total);
            System.out.println("Time elapsed: " +
                Duration.between(startTime, endTime).toMillis() + " ms");

            // 创建搜索任务列表，不是所有文件都包含关键字，所以不是所有任务都成功
            var searchTasks = new ArrayList<Callable<Path>>();
            for (Path file : files) {
                Callable<Path> task = searchForTask(word, file);
                searchTasks.add(task);
            }

            // 任意一个任务成功，就返回结果
            Path found = executor.invokeAny(searchTasks);
            System.out.println("Found in: " + found);

            if (executor instanceof ThreadPoolExecutor tpExecutor) {
                // 获取线程池中的最大线程数
                System.out.println("Largest pool size: " + tpExecutor.getLargestPoolSize());
            }

            // 等待所有任务完成后关闭所有线程
            executor.shutdown();
        }
    }

    // word出现在path文件中的次数
    public static long occurrences(String word, Path path) {
        try (var in = new Scanner(path)) {
            int count = 0;
            while (in.hasNext()) {
                if (in.next().equals(word)) {
                    count++;
                }
            }
            return count;
        } catch (IOException ex) {
            return 0;
        }
    }

    public static Set<Path> descendants(Path rootDir) throws IOException {
        try (Stream<Path> entries = Files.walk(rootDir)) {
            return entries.filter(Files::isRegularFile).collect(Collectors.toSet());
        }
    }

    public static Callable<Path> searchForTask(String word, Path path) {
        return () -> {
            try (var in = new Scanner(path)) {
                while (in.hasNext()) {
                    if (in.next().equals(word)) {
                        return path;
                    }
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Search in " + path + " canceld");
                        return null;
                    }
                }
                throw new NoSuchElementException();
            }
        };
    }
}
